<?php
class Lead_EDO_Handler_Persist_Functional_UnitOfWork
{
    private $_clean = array();
    private $_new = array();
    private $_dirty = array();
    private $_deleted = array();
    
    private $_db;
    private $_identityMap;
        
    public function __construct($db, $identityMap)
    {
        $this->_db = $db;
        $this->_identityMap = $identityMap;
    }
    
    
    public function registerClean(Lead_EDO_Object_Abstract $edo)
    {
        $this->_clean[] = $edo->id;
    }
    
    public function registerDirty(Lead_EDO_Object_Abstract $edo)
    {
        if(in_array($edo->id, $this->_deleted)) {
            throw new Lead_EDO_Exception("Cannot save a deleted object with id: {$edo->id}");
        }
        
        $this->_dirty[] = $edo->id;
    }
    

    public function registerNew(Lead_EDO_Object_Abstract $edo)
    {
        $this->_new[] = $edo->id;
    }
    
    
    public function registerDeleted(Lead_EDO_Object_Abstract $edo)
    {
        $this->_clean = array_diff($this->_clean, array($edo->id));
        $this->_dirty = array_diff($this->_clean, array($edo->id));        
        $this->_deleted = array_merge($this->_deleted, array($edo->id));
    }
        
    private function _delete(Lead_EDO_Object_Abstract $obj)
	{
	  		    
	    $phrase = "DELETE FROM edo WHERE id = {$obj->getField('id')}";
		$errorfree = array();
		$this->_db->beginTransaction();
		
		/*
		$fields = $obj->getFields();
		foreach($fields as &$fieldname)
		{
			$fieldObj = $obj->getFieldObject($fieldname);
			if(is_callable(array($fieldObj, 'delete')))
			{
				$res = $fieldObj->delete();
				$errorfree[] = $res;
			}
		}
		*/

		$errorfree = $this->_db->query($phrase);
		$errorfree ? $this->_db->commit() : $this->_db->rollBack();

		if($errorfree)
		{
		    // $cache = $this->getCacheFrontEnd();
			// $cache->remove("edo_{$id}");

		}

		return $errorfree ? true : false;
	}
    
	
	
	private function _save(Lead_EDO_Object_Abstract $obj)
	{
	    if($dasField = $obj->getFieldObject('automatic_modified_on'))
		{
		    $dasField->setValue(date('Y-m-d H:i:s', time()));
		}

	    $this->_db->beginTransaction();
		$errorfree = array();
		foreach($obj->getType()->getTables() as $tablekey => $tablename)
		{
			$darray = array();
			foreach($obj->getType()->getTablesFields($tablekey) as $field)
			{
				$darray[$field] = $this->_db->formatFieldForSQL($obj->getFieldObject($field));
			}
            
			try {
                if($obj->getType()->tableIsDimensional($tablename))
			        $errorfree[] = $this->_db->update($tablename, $darray, "id = {$obj->getField('id')} AND dimension_id = {$obj->getField('dimension_id')}");
			    else			    
			        $errorfree[] = $this->_db->update($tablename, $darray, "id = {$obj->getField('id')}");    
			} catch (Zend_Db_Exception $e) {
                			    			    
			    $errorfree[] = false;
                break;    
			}
			
		}

		$errorfree = (!in_array(false, $errorfree, true));
		$errorfree ? $this->_db->commit() : $this->_db->rollBack();

		
		if($errorfree)
		{
			// $cache = $this->getCacheFrontEnd();
			// $cache->save($obj, "edo_{$obj->id}");

			return true;
		}

		return false;




	}
	
	private function _insert(Lead_EDO_Object_Abstract $obj)
	{
	    
	    if($dasField = $obj->getFieldObject('automatic_created_on')) {
		    $dasField->setValue(date('Y-m-d H:i:s', time()));
		}
		
	   	if($dasField = $obj->getFieldObject('automatic_modified_on')) {
       	    $dasField->setValue(date('Y-m-d H:i:s', time()));
	    }
			

	    $id = $obj->id;
	    $typeid = $obj->getTypeId();
	    	    
	    // $id = $obj->getField('id');
	    
        // $id = $this->_db->nextId('id');
		// $obj->setField('id', $id);
	    
	    
	    /*
		if($id === false OR is_null($id) OR $clear) {
			$id = $this->_db->nextId('id');
			$obj->setField('id', $id);
		} else {
			$id = $obj->getField('id');
		}
		*/

		$this->_db->beginTransaction();

		
		
		$errorfree = array();

		$phrase = "INSERT INTO edo (id, type_id) VALUES ({$id}, {$typeid})";
		
		$errorfree[] = $this->_db->query($phrase);
		
		foreach($obj->getType()->getTables() as $tablekey => $tablename) {
			$darray = array();
			
			foreach($obj->getType()->getTablesFields($tablekey) as $field) {
				$darray[$field] = $this->_db->formatFieldForSQL($obj->getFieldObject($field));
			}
            
			if($obj->getType()->tableIsDimensional($tablename)) {
			    $darray['dimension_id'] = call_user_func($obj->getHandler()->getAttribute('Callback_GetDimension'));
			}
                
              
            $errorfree[] = $this->_db->insert($tablename, $darray);

		}
		$errorfree = (!in_array(false, $errorfree, true));


		$errorfree ? $this->_db->commit() : $this->_db->rollBack();

		if($errorfree)
		{
			// $cache = $this->getCacheFrontEnd();
			// $cache->save($obj, "edo_{$id}");
			return $id;
		}
		else
		{
			return false;
		}



	}
	
	
	public function rollback()
	{
	    $this->_clean = array();
	    $this->_dirty = array();
	    $this->_deleted = array();
	    $this->_new = array();  
	}
	
	// todo: optimize like hell!
	public function commit()
	{
	    /*
	    print "<pre>";

	    print "<h1>Clean</h1>";
	    print_r($this->_clean);
	    
	    print "<h1>Dirty</h1>";
	    print_r($this->_dirty);
	    
	    print "<h1>New</h1>";
	    print_r($this->_new);
	    
	    print "<h1>Deleted</h1>";
	    print_r($this->_deleted);
	   	
		print "</pre>";
	   	
		*/	
		    
	    // insert only if newly inserted isn't already been deleted
	    foreach($this->_new as $id) {
	        if(!in_array($id, $this->_deleted))
	            $this->_insert($this->_identityMap->get($id));
	    }
	    
	    // save all dirty objects. Todo: make objects confess their dirtiness.
	    foreach($this->_dirty as $id) {
	        $this->_save($this->_identityMap->get($id));
	    }
	    	    
	    
	    foreach($this->_deleted as $id) {
	        // newly inserted and deleted are not inserted or deleted
	        if(!in_array($id, $this->_new))
	            $this->_delete($this->_identityMap->get($id));
	    }
	   
	    
	    $this->_clean = array();
	    $this->_dirty = array();
	    $this->_deleted = array();
	    $this->_new = array();  
	    
	    
	    
	}
	
    
	
	public function __destruct()
	{
	    $this->commit();
	}
	
	
}